import CryptoJS from 'crypto-js'

// 和 cam 保持一致的 url encode
function camSafeUrlEncode(str) {
  return encodeURIComponent(str)
    .replace(/!/g, '%21')
    .replace(/'/g, '%27')
    .replace(/\(/g, '%28')
    .replace(/\)/g, '%29')
    .replace(/\*/g, '%2A')
}

const HmacSHA1 = function (text, key) {
  return CryptoJS.HmacSHA1(text, key).toString()
}
const SHA1 = function (text) {
  return CryptoJS.SHA1(text).toString()
}
const base64 = function (text) {
  return CryptoJS.enc.Utf8.parse(text).toString(CryptoJS.enc.Base64)
}

// v4 签名
const CosAuthV4 = function (opt) {
  let pathname = opt.Pathname || '/'
  let expires = opt.Expires

  let ShortBucketName = ''
  let AppId = ''
  let match = opt.Bucket.match(/^(.+)-(\d+)$/)
  if (match) {
    ShortBucketName = match[1]
    AppId = match[2]
  }

  let random = parseInt(Math.random() * 2 ** 32)
  let now = parseInt(Date.now() / 1000)
  let e = now + (expires === undefined ? 900 : (expires * 1 || 0)) // 默认签名过期时间为当前时间 + 900s
  let path = `/${AppId}/${ShortBucketName}${encodeURIComponent(pathname).replace(/%2F/g, '/')}` //多次签名这里填空
  let plainText = `a=${AppId}&b=${ShortBucketName}&k=${opt.SecretId}&e=${e}&t=${now}&r=${random}&f=${path}`
  let sha1Res = CryptoJS.HmacSHA1(plainText, opt.SecretKey)
  let strWordArray = CryptoJS.enc.Utf8.parse(plainText)
  let resWordArray = sha1Res.concat(strWordArray)
  let sign = resWordArray.toString(CryptoJS.enc.Base64)
  // console.log('sign:', sign)
  let res = HmacSHA1(plainText, opt.SecretKey)
  // console.log('res:', res)
  // console.log('sign1:', base64(res))
  // console.log('sign2:', base64(plainText))
  // console.log('sign2:', base64(res + plainText))

  return sign
}

// PostObject policy 签名
const CosAuthPolicy = function (opt) {
  let now = Math.round(Date.now() / 1000)
  let exp = now + (opt.Expires || 900)
  let qKeyTime = `${now};${exp}`
  let qSignAlgorithm = 'sha1'
  let policy = JSON.stringify({
    'expiration': new Date(exp * 1000).toISOString(),
    'conditions': [
      // {'acl': query.ACL},
      // ['starts-with', '$Content-Type', 'image/'],
      // ['starts-with', '$success_action_redirect', redirectUrl],
      // ['eq', '$x-cos-server-side-encryption', 'AES256'],
      { 'q-sign-algorithm': qSignAlgorithm },
      { 'q-ak': opt.SecretId },
      { 'q-sign-time': qKeyTime },
      { 'bucket': opt.Bucket },
      { 'key': opt.Key },
    ],
  })

  // 签名算法说明文档：https://www.qcloud.com/document/product/436/7778
  // 步骤一：生成 SignKey
  let signKey = HmacSHA1(qKeyTime, opt.SecretKey)

  // 步骤二：生成 StringToSign
  let stringToSign = SHA1(policy)

  // 步骤三：生成 Signature
  let qSignature = HmacSHA1(stringToSign, signKey)

  let credentials = {
    policyObj: JSON.parse(policy),
    policy: base64(policy),
    qSignAlgorithm,
    qAk: opt.SecretId,
    qKeyTime,
    qSignature,
  }

  return credentials
}

// v5 签名
const CosAuth = function (opt) {

  if (!opt.SecretId) return console.error('missing param SecretId')
  if (!opt.SecretKey) return console.error('missing param SecretKey')

  if (opt.Version === '4.0') {
    return CosAuthV4(opt)
  } else if (opt.Version === 'post-object-policy') {
    return CosAuthPolicy(opt)
  }

  opt = opt || {}

  let SecretId = opt.SecretId
  let SecretKey = opt.SecretKey
  let method = (opt.Method || 'get').toLowerCase()
  let query = opt.Query || {}
  let headers = opt.Headers || {}
  let pathname = opt.Pathname || '/'
  let expires = opt.Expires

  const getObjectKeys = function (obj) {
    let list = []
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        list.push(key)
      }
    }
    return list.sort((a, b) => {
      a = a.toLowerCase()
      b = b.toLowerCase()
      return a === b ? 0 : (a > b ? 1 : -1)
    })
  }

  const obj2str = function (obj, lowerCaseKey) {
    let i, key, val
    let list = []
    let keyList = getObjectKeys(obj)
    for (i = 0; i < keyList.length; i++) {
      key = keyList[i]
      val = (obj[key] === undefined || obj[key] === null) ? '' : (`${obj[key]}`)
      key = lowerCaseKey ? camSafeUrlEncode(key).toLowerCase() : camSafeUrlEncode(key)
      val = camSafeUrlEncode(val) || ''
      list.push(`${key}=${val}`)
    }
    return list.join('&')
  }

  // 签名有效起止时间
  let now = parseInt(new Date().getTime() / 1000) - 1
  let exp = now + (expires === undefined ? 900 : (expires * 1 || 0)) // 默认签名过期时间为当前时间 + 900s

  // 要用到的 Authorization 参数列表
  let qSignAlgorithm = 'sha1'
  let qAk = SecretId
  let qSignTime = `${now};${exp}`
  let qKeyTime = `${now};${exp}`
  let qHeaderList = getObjectKeys(headers).join(';').toLowerCase()
  let qUrlParamList = getObjectKeys(query).join(';').toLowerCase()

  // 签名算法说明文档：https://www.qcloud.com/document/product/436/7778
  // 步骤一：计算 SignKey
  let signKey = HmacSHA1(qKeyTime, SecretKey)

  // 步骤二：构成 FormatString
  let formatString = [method, pathname, obj2str(query, true), obj2str(headers, true), ''].join('\n')

  // 步骤三：计算 StringToSign
  let stringToSign = ['sha1', qSignTime, SHA1(formatString), ''].join('\n')

  // 步骤四：计算 Signature
  let qSignature = HmacSHA1(stringToSign, signKey)

  // 步骤五：构造 Authorization
  let authorization = [
    `q-sign-algorithm=${qSignAlgorithm}`,
    `q-ak=${qAk}`,
    `q-sign-time=${qSignTime}`,
    `q-key-time=${qKeyTime}`,
    `q-header-list=${qHeaderList}`,
    `q-url-param-list=${qUrlParamList}`,
    `q-signature=${qSignature}`,
  ].join('&')

  return authorization

}

CosAuth.HmacSHA1 = HmacSHA1
CosAuth.SHA1 = SHA1
CosAuth.base64 = base64

export default CosAuth
